package ru.ifmo.rybakov.muclumon.analysis2;

import java.util.Iterator;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;


public class AglomerativeClusteringAlgorithm {
	
	private static final Logger logger = LoggerFactory.getLogger(AglomerativeClusteringAlgorithm.class);
	
	/**
	 * Runs on the given set of clusters until finish condition is met, then
	 * returns resulting set of clusters.
	 * @param clusters Initial set of clusters
	 * @return Resulting set of clusters
	 */
	public Cluster[] run(Cluster[] clusters, ClusterComparator comparator, ClusterDecisionStrategy decisionStrategy) {
		
		while (clusters.length > 1) {
			double minDistance = Double.MAX_VALUE;
			int c1 = -1, c2 = -1;
			
			// look for two clusters to merge
			for (int i = 0; i < clusters.length; i++) {
				for (int j = i + 1; j < clusters.length; j++) {
					double distance = comparator.getDistance(clusters[i], clusters[j]);
					if (distance < minDistance) {
						minDistance = distance;
						c1 = i;
						c2 = j;
					}
				}
			}
			
			logger.debug("Minimum distance: {}", minDistance);
			
			// check if it is time to stop
			if (decisionStrategy.haveToStop(minDistance)) {
				break;
			}
			
			// now merge two clusters
			Cluster[] newClusters = new Cluster[clusters.length - 1];
			int n = 0;
			for (int i = 0; i < c1; i++, n++) {
				newClusters[n] = clusters[i];
			}
			for (int i = c1 + 1; i < c2; i++, n++) {
				newClusters[n] = clusters[i];
			}
			for (int i = c2 + 1; i < clusters.length; i++, n++) {
				newClusters[n] = clusters[i];
			}
			newClusters[n] = clusters[c1].merge(clusters[c2]);
			
			// now debug
			for (int i = 0; i < newClusters.length; i++) {
				Iterator<?> it = newClusters[i].getObjects().iterator();
				System.out.print("{");
				while (it.hasNext()) {
					String message = it.next().toString();
					System.out.print(message);
					if (it.hasNext()) System.out.print(",");
				}
				System.out.print("}");
			}
			System.out.println();
			
			clusters = newClusters;
		}
		
		return clusters;
	}
}
